Repository navigation
An executable and the JIT say what an exception nothing caught threw (Windows and Linux) - #833
Merged
Merged
Conversation
An uncaught throw in a Windows executable reached the C runtime's terminate(), whose abort() ended the program with exit code 3 (0xC0000409 with --opt) and not a word - not even what it had printed, still in stdout's buffer. That is how JSON.stringify's TypeError for an object literal looked like a crash. The JIT already said "error: uncaught exception: <what>". Now an executable says the same: TypeScriptAsyncRuntime/UncaughtException.cpp puts an unhandled-exception filter in front of the C runtime's, which flushes stdout, writes the description to stderr and ends the program with exit code 1. tslang links it into every executable with -include:__tslang_report_uncaught, when the runtime has it (an older runtime links as before). The description (a string, a number as the program prints it, a class by its name, an Error with its message) moved from jit.cpp to TypeScript/UncaughtException.h, which both use. Tests: uncaught-exe.cmake, the four uncaught/*.ts programs under gc, rc, none and own, with and without --opt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… threw
On Linux an uncaught throw went to libstdc++'s terminate handler ("terminate called after
throwing an instance of 'char const*'") and SIGABRT, and the program's buffered output was
lost; under the JIT LLVM's crash handler reported it. Now the runtime's UncaughtException.cpp
sets a std::terminate handler there, linked into an executable with `-u __tslang_report_uncaught`
(when the runtime has it), and the JIT sets the same around the entry call: stdout flushed,
"error: uncaught exception: <what>" on stderr, exit code 1.
describeCurrentException (TypeScript/UncaughtException.h) reads the current exception from
std::current_exception() and its type_info's name (`PKc`, `d`, `P7Failure`, ...); an Error's
message is found through the class type_info's base chain. tslang is built without RTTI, so the
Itanium layouts are read as they are.
The uncaught tests (JIT and executable) now run on Linux too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An uncaught exception used to end a program without saying what was thrown.
terminate()calledabort(), which exited silently with 3, or 0xC0000409 with--opt.terminate called after throwing an instance of 'char const*'and the program died of SIGABRT. Under the JIT, LLVM's crash handler reported it as a crash of the compiler.Either way the program's earlier output, still in stdout's buffer, was lost. That is why
JSON.stringify({x: 1})looked like a crash (fixed in #832). The Windows JIT already reportederror: uncaught exception: <what>.Now, as an executable or under the JIT, on Windows and Linux:
beforeon stdouterror: uncaught exception: TypeError: boomon stderr (the JIT adds itstslang:prefix)Change
TypeScriptAsyncRuntime/UncaughtException.cpp(new): its initializer sets the handler.std::terminatehandler.terminate()called with no exception (e.g. a joinablestd::threaddestroyed) goes to the previous handler, as before.TerminateProcess/_exit), as the JIT does.tslang/exe.cpp: every executable is linked with-include:__tslang_report_uncaught(Windows) or-Wl,-u,__tslang_report_uncaught(Linux), placed before the runtime archive. The archive is checked for the symbol first, so a runtime older than this links as before. Android is unchanged.tslang/jit.cpp: on Linux the JIT sets the same terminate handler around the entry call.TypeScript/UncaughtException.h(new): the code that describes the value, which both use. It describes:Error, with its messageOn Windows it reads the exception record, as
jit.cppdid; that code is moved here unchanged. On Linux it reads the current exception throughstd::current_exception()and its type_info name (PKc,d,P7Failure, …). AnErroris found by walking the class type_info's base chain. tslang is built without RTTI, so the Itanium layouts are read directly.Tests
uncaught-exe.cmakebuilds the fouruncaught/*.tsprograms with--emit=exe, under gc, rc, none and own, with and without--opt: 32 tests. Each checks exit code 1,beforeon stdout and the description on stderr.Verified locally
tests.ps1passes for all four models, release and debug, compiled and JIT. This run predates the Linux commit, which leaves the Windows code as it was.throw new TypeError("boom")printserror: uncaught exception: TypeError: boom.test-compile-shared-import-chain-rtti-skipped, a type-checking error ("Class member 'reports' can't be found") in a test unrelated to this change. It passed in this PR's earlier Linux CI run, which had the same compiler front end, and looks specific to the reused local WSL build tree. Linux CI will confirm.Not covered
tslang --emit=exedoes.🤖 Generated with Claude Code